/*
 * Copyright 2007-2008 Andrew O'Malley
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package ws.quokka.plugin.devreport;

import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectComponent;
import org.apache.tools.ant.types.ResourceCollection;

import ws.quokka.core.bootstrap_util.Logger;
import ws.quokka.core.bootstrap_util.PropertiesUtil;
import ws.quokka.core.bootstrap_util.Reflect;
import ws.quokka.core.plugin_spi.Resources;
import ws.quokka.core.plugin_spi.support.ResourcesList;
import ws.quokka.core.plugin_spi.support.TypedProperties;

import java.io.File;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;


/**
 *
 */
public class DevReportHelper {
    //~ Static fields/initializers -------------------------------------------------------------------------------------

    public static final String TARGET = "dev-reports";
    private static final String TYPE_LOCAL = "local";
    private static final String TYPE_AGGREGATED = "aggregated";
    private static final String DEFAULT = "default";

    //~ Instance fields ------------------------------------------------------------------------------------------------

    private DevReportsState state;
    private TypedProperties globalProperties;
    private TypedProperties properties;
    private Logger logger;
    private boolean aggregate;
    private Resources resources;

    //~ Constructors ---------------------------------------------------------------------------------------------------

    public DevReportHelper(Resources resources, TypedProperties properties, Logger logger) {
        Map state = (Map)resources.getPluginState().get(DevReportsState.class.getName(), new HashMap());
        this.state = new DevReportsState(state);
        this.resources = resources;
        this.properties = properties;
        this.logger = logger;

        globalProperties = new TypedProperties("q.devreport.", PropertiesUtil.getProperties(resources.getProject()),
                resources.getProject());

        aggregate = properties.getBoolean("aggregate", false);
    }

    //~ Methods --------------------------------------------------------------------------------------------------------

    protected ResourcesList getAggregation(String key) {
        return getResources(resources.getProject(), TYPE_AGGREGATED, key);
    }

    protected void addResource(Project project, String type, String key, ResourceCollection resource) {
        getResources(project, type, key).add(resource);
    }

    protected ResourcesList getResources(Project project, String type, String key) {
        Map reportResources = getReportResources(project);

        key = resources.getNameSpace() + "." + type + "." + key;

        ResourcesList resources = (ResourcesList)reportResources.get(key);

        if (resources == null) {
            resources = new ResourcesList(project);
            reportResources.put(key, resources);
        }

        return resources;
    }

    protected Map getReportResources(Project project) {
        String key = "q.devreport.resources";
        Map resources = (Map)project.getReference(key);

        if (resources == null) {
            resources = new HashMap();
            project.addReference(key, resources);
        }

        return resources;
    }

    public DevReportsState getState() {
        return state;
    }

    public TypedProperties getProperties() {
        return globalProperties;
    }

    public File getReportsFile() {
        return globalProperties.getFile("reportsFile");
    }

    public File getReportsRoot() {
        return globalProperties.getFile("reportsRootDir");
    }

    public Report addReport() {
        return addReport(properties);
    }

    public void addReport(Report report) {
        getState().addReport(report);
    }

    public Report addReport(TypedProperties properties) {
        Report report = createReport(properties);
        logger.info("Adding report: " + report.getName());
        getState().addReport(report);

        if (logger.isDebugEnabled()) {
            logger.debug("Project: " + resources.getProject().getName() + ": "
                + System.identityHashCode(resources.getProject()));

            for (Iterator i = getState().getReports().iterator(); i.hasNext();) {
                Report report1 = (Report)i.next();

                if (logger.isDebugEnabled()) {
                    logger.debug("reports: " + report1.getName());
                }
            }
        }

        return report;
    }

    public Report createReport() {
        return createReport(properties);
    }

    public Report createReport(TypedProperties properties) {
        return new Report(properties.getString("name"), properties.getString("description"),
            properties.getFile("indexFile"));
    }

    public boolean isAggregate() {
        return aggregate;
    }

    public void addResource(String name, ResourceCollection resourceCollection) {
        addResource(resources.getProject(), TYPE_LOCAL, name, resourceCollection);

        if (aggregate) {
            addResource(resources.getProject(), TYPE_AGGREGATED, name, resourceCollection);
        }
    }

    public void addResource(ResourceCollection resourceCollection) {
        addResource(DEFAULT, resourceCollection);
    }

    public ResourcesList getLocalResources(String name) {
        return getResources(resources.getProject(), TYPE_LOCAL, name);
    }

    public ResourcesList getAggregatedResources() {
        return getResources(resources.getProject(), TYPE_AGGREGATED, DEFAULT);
    }

    public ResourcesList getAggregatedResources(String name) {
        return getResources(resources.getProject(), TYPE_AGGREGATED, name);
    }

    public ResourcesList getLocalResources() {
        return getLocalResources(DEFAULT);
    }

    public void propogateResources() {
        propogateResources(DEFAULT);
    }

    public void propogateResources(String name) {
        if (resources.getParentProject() == null) {
            logger.verbose("Skipping resource propogation as there is no parent");
        } else {
            ResourcesList resourcesList = getResources(resources.getProject(), TYPE_AGGREGATED, name);
            ResourcesList parentResources = getResources(resources.getParentProject(), TYPE_AGGREGATED, name);

            for (Iterator i = resourcesList.getNested().iterator(); i.hasNext();) {
                ResourceCollection collection = (ResourceCollection)i.next();
                ResourceCollection clone = (ResourceCollection)new Reflect().clone(collection);

                if (clone instanceof ProjectComponent) {
                    ((ProjectComponent)clone).setProject(resources.getParentProject());
                }

                parentResources.add(collection);
                logger.info("Propogating '" + name + "': " + collection);
            }
        }
    }

    public void dumpResources(String message, Project project) {
        if (logger.isDebugEnabled()) {
            logger.debug(message + project.getName() + ": " + System.identityHashCode(project));

            for (Iterator i = getReportResources(project).entrySet().iterator(); i.hasNext();) {
                Map.Entry entry = (Map.Entry)i.next();
                logger.debug("   " + entry + " -> " + entry.getValue());
            }
        }
    }

    public void dumpResources() {
        dumpResources(" Local Project: ", resources.getProject());
        dumpResources("Parent Project: ", resources.getParentProject());
    }

    public String getRelativePath(Report report, File root) {
        String relativePath = report.getIndexFile().getAbsolutePath();

        if (relativePath.startsWith(root.getAbsolutePath())) {
            relativePath = relativePath.substring(root.getAbsolutePath().length() + 1);
        }

        relativePath = relativePath.replace('\\', '/');

        return relativePath;
    }
}
